# -- coding: utf-8 --

import sys
import copy
import os
import cv2
import termios
import numpy as np
import datetime
from ctypes import *

sys.path.append("../MvImport")
from MvCameraControl_class import *

winfun_ctype = CFUNCTYPE

stFrameInfo = POINTER(MV_FRAME_OUT_INFO_EX)
pData = POINTER(c_ubyte)
FrameInfoCallBack = winfun_ctype(None, pData, stFrameInfo, c_void_p)



# Video writer initialization
video_writer = None
def image_callback(pData, pFrameInfo, pUser):
		print('=========================================')
		global video_writer
		# 获取当前时间
		now = datetime.datetime.now()
		start_time = now.replace(hour=16, minute=5, second=0, microsecond=0)
		end_time = now.replace(hour=18, minute=54, second=0, microsecond=0)

		stFrameInfo = cast(pFrameInfo, POINTER(MV_FRAME_OUT_INFO_EX)).contents
		if stFrameInfo:
			# 假设图像格式为 BGR8 或 RGB8 格式
			# 获取图像宽度、高度
			width = stFrameInfo.nWidth
			height = stFrameInfo.nHeight
			nHostTimeStamp = stFrameInfo.nHostTimeStamp
			nFrameNum = int(stFrameInfo.nFrameNum)
			print("nFrameNum:",nFrameNum)

			nFrameLen = int(stFrameInfo.nFrameLen)
			print("nFrameLen:",nFrameLen)

			nFrameCounter = int(stFrameInfo.nFrameCounter)
			print("nFrameCounter:",nFrameCounter)

			if pData is None:
				raise ValueError("pData is None")

			image_size = width * height * 3  # 对于 BGR/RGB，每个像素 3 字节
			img_buff = (c_ubyte * image_size)()  # 分配内存
			# memmove(img_buff, pData, image_size)  # 将 pData 的内容复制到 img_buff
			'''
			pData返回的是一个地址指针，建议将pData里面的数据copy出来另建线程使用。memmove(img_buff, pData, image_size)  将 pData 的内容复制到 img_buff
			nFrameLen 和 image_size 的值通常会不相等
			nFrameLen 是指帧数据的实际字节长度，它通常包括了图像的所有信息，例如头信息、附加数据、压缩数据等。因此，这个值可能大于或小于单纯的图像数据的字节数。
			image_size 是根据图像的宽度和高度计算得出的，假设每个像素使用 3 个字节（RGB/BGR 格式），计算公式为：image_size=width×height×3 这个值仅表示图像本身的大小，不包括其他数据或元数据。
			'''
			if nFrameLen>=image_size:
				memmove(img_buff, pData, image_size)  # 将 pData 的内容复制到 img_buff
			else:
				memmove(img_buff, pData, min(image_size, nFrameLen))   # 将 pData 的内容复制到 img_buff
			# 使用 numpy 解析图像数据
			img_array = np.ctypeslib.as_array(img_buff)
			print(img_array.shape)
			img_array = img_array.reshape((height, width, 3))  # 转换为 3 通道的图像格式

			# 转换为 OpenCV 格式
			img_cv = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)  # 如果图像格式为 RGB，需要转换为 BGR
			# data = np.asarray(pData)
			# image = image_control(data,stFrameInfo)
			cv2.imwrite('frame3.jpg',img_cv)
			print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
			 # 检查是否在 9:30 到 10:30 之间
			if start_time <= now <= end_time:
				print(now,start_time,end_time)
				print('statr  write  frame ........')
				if video_writer is None:
					# 初始化视频写入器，设置视频保存路径和参数
					fourcc = cv2.VideoWriter_fourcc(*'XVID')  # 使用 XVID 编码器
					video_writer = cv2.VideoWriter(f'output_{now.strftime("%Y%m%d_%H%M%S")}.avi', fourcc, fResultFrameRate, (stFrameInfo.nWidth, stFrameInfo.nHeight))
				 
				video_writer.write(img_cv)
			
			else:
				if video_writer is not None:
					# 超出保存时间，释放视频写入器
					video_writer.release()
					video_writer = None

CALL_BACK_FUN = FrameInfoCallBack(image_callback)


def press_any_key_exit():
	fd = sys.stdin.fileno()
	old_ttyinfo = termios.tcgetattr(fd)
	new_ttyinfo = old_ttyinfo[:]
	new_ttyinfo[3] &= ~termios.ICANON
	new_ttyinfo[3] &= ~termios.ECHO
	#sys.stdout.write(msg)
	#sys.stdout.flush()
	termios.tcsetattr(fd, termios.TCSANOW, new_ttyinfo)
	try:
		os.read(fd, 7)
	except:
		pass
	finally:
		termios.tcsetattr(fd, termios.TCSANOW, old_ttyinfo)

# 显示图像
def image_show(image):
	# image = cv2.resize(image, (600, 400), interpolation=cv2.INTER_AREA)  # 对图片进行裁剪
	cv2.imshow('fgmask', image)
	cv2.waitKey(1)

# 需要显示的图像数据转换
def image_control(data, stFrameInfo,show_image=False):
	if stFrameInfo.enPixelType == 17301505:  # 17301505为黑白图片 mono
		image = data.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
		if show_image:
			image_show(image=image)

	elif stFrameInfo.enPixelType == 17301514:  # 17301514为Bater图像
		data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
		image = cv2.cvtColor(data, cv2.COLOR_BAYER_GB2RGB)
		if show_image:
			image_show(image=image)

	elif stFrameInfo.enPixelType == 35127316:  # 35127316原始图像通道是RGB的,设置RGB8
		data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
		image = data
		if show_image:
			image = cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
			image_show(image=image)
				 
	elif stFrameInfo.enPixelType == 34603039:  # 34603039 为YUV图像
		data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
		image = cv2.cvtColor(data, cv2.COLOR_YUV2BGR_Y422)
		if show_image:
			image_show(image=image)

	elif stFrameInfo.enPixelType == 17301513:  # 17301513 为BayerRG8
		data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
		image = cv2.cvtColor(data, cv2.COLOR_BAYER_GB2RGB)
		if show_image:
			image_show(image=image)
	

	else:
		data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
		image = cv2.cvtColor(data, cv2.COLOR_BGR2RGB)
		if show_image:
			image = cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
			image_show(image=image)

	return image

if __name__ == "__main__":

	# ch:初始化SDK | en: initialize SDK
	MvCamera.MV_CC_Initialize()

	SDKVersion = MvCamera.MV_CC_GetSDKVersion()
	print ("SDKVersion[0x%x]" % SDKVersion)

	deviceList = MV_CC_DEVICE_INFO_LIST()
	tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
	
	# ch:枚举设备 | en:Enum device
	ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
	if ret != 0:
		print ("enum devices fail! ret[0x%x]" % ret)
		sys.exit()
	
	if deviceList.nDeviceNum == 0:
		print ("find no device!")
		sys.exit()

	print ("find %d devices!" % deviceList.nDeviceNum)

	for i in range(0, deviceList.nDeviceNum):
		mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
		if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
			print ("\ngige device: [%d]" % i)
			strModeName = ""
			for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
				strModeName = strModeName + chr(per)
			print ("device model name: %s" % strModeName)

			nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
			nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
			nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
			nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
			print ("current ip: %d.%d.%d.%d\n" % (nip1, nip2, nip3, nip4))
		elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
			print ("\nu3v device: [%d]" % i)
			strModeName = ""
			for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
				if per == 0:
					break
				strModeName = strModeName + chr(per)
			print ("device model name: %s" % strModeName)

			strSerialNumber = ""
			for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
				if per == 0:
					break
				strSerialNumber = strSerialNumber + chr(per)
			print ("user serial number: %s" % strSerialNumber)

	if sys.version >= '3':
		nConnectionNum = input("please input the number of the device to connect:")
	else:
		nConnectionNum = raw_input("please input the number of the device to connect:")

	if int(nConnectionNum) >= deviceList.nDeviceNum:
		print ("intput error!")
		sys.exit()
	
	# ch:创建相机实例 | en:Creat Camera Object
	cam = MvCamera()

	# ch:选择设备并创建句柄 | en:Select device and create handle
	stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents

	ret = cam.MV_CC_CreateHandle(stDeviceList)
	if ret != 0:
		print ("create handle fail! ret[0x%x]" % ret)
		sys.exit()

	# ch:打开设备 | en:Open device
	ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
	if ret != 0:
		print ("open device fail! ret[0x%x]" % ret)
		sys.exit()
	
	# ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
	if stDeviceList.nTLayerType == MV_GIGE_DEVICE:
		nPacketSize = cam.MV_CC_GetOptimalPacketSize()
		if int(nPacketSize) > 0:
			ret = cam.MV_CC_SetIntValue("GevSCPSPacketSize",nPacketSize)
			if ret != 0:
				print ("Warning: Set Packet Size fail! ret[0x%x]" % ret)
		else:
			print ("Warning: Get Packet Size fail! ret[0x%x]" % nPacketSize)

	# ch:设置触发模式为off | en:Set trigger mode as off
	ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
	if ret != 0:
		print ("set trigger mode fail! ret[0x%x]" % ret)
		

	## 获取相机采集帧率
	fAcquiFrameRate = c_float()
	nRet = cam.MV_CC_GetFloatValue('AcquisitionFrameRate', fAcquiFrameRate)
	if nRet == 0:
		fAcquiFrameRate = fAcquiFrameRate.value
		print("Acquisition Frame Rate:", fAcquiFrameRate)
	else:
		print("Failed to get frame rate")
		sys.exit()
	
	## 获取相机实际帧率
	fResultFrameRate = c_float()
	nRet = cam.MV_CC_GetFloatValue('ResultingFrameRate', fResultFrameRate)
	if nRet == 0:
		fResultFrameRate = fResultFrameRate.value
		print("Resulting Frame Rate:", fResultFrameRate)
	else:
		print("Failed to get frame rate")
		sys.exit()


	# ch:注册抓图回调 | en:Register image callback
	ret = cam.MV_CC_RegisterImageCallBackEx(CALL_BACK_FUN,None)
	if ret != 0:
		print ("register image callback fail! ret[0x%x]" % ret)
		sys.exit()

	# ch:开始取流 | en:Start grab image
	ret = cam.MV_CC_StartGrabbing()
	if ret != 0:
		print ("start grabbing fail! ret[0x%x]" % ret)

	print ("press a key to stop grabbing.")
	press_any_key_exit()

	# ch:停止取流 | en:Stop grab image
	ret = cam.MV_CC_StopGrabbing()
	if ret != 0:
		print ("stop grabbing fail! ret[0x%x]" % ret)
		sys.exit()

	# ch:关闭设备 | Close device
	ret = cam.MV_CC_CloseDevice()
	if ret != 0:
		print ("close deivce fail! ret[0x%x]" % ret)
		sys.exit()

	# ch:销毁句柄 | Destroy handle
	ret = cam.MV_CC_DestroyHandle()
	if ret != 0:
		print ("destroy handle fail! ret[0x%x]" % ret)
		sys.exit()

	# ch:反初始化SDK | en: finalize SDK
	MvCamera.MV_CC_Finalize()
